/*
 * Copyright (c) Jiandong.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { PackagerStatusCallback } from './devSupport/PackagerStatusCallback';

import Want from '@ohos.app.ability.Want';
import hilog from '@ohos.hilog';
import Window from '@ohos.window'
import worker from '@ohos.worker'
import ArrayList from '@ohos.util.ArrayList'
import Context from '@ohos.application.context';
import HashMap from '@ohos.util.HashMap';
import emitter from '@ohos.events.emitter';

import { DevSupportManager } from './devSupport/DevSupportManager';
import { ReactInstanceDevHelper } from './devSupport/ReactInstanceDevHelper';
import { RequestHandler } from './packagerConnection/RequestHandler';
import { DevBundleDownloadListener } from './devSupport/DevBundleDownloadListener';
import { DevSupportManagerFactory } from './devSupport/DevSupportManagerFactory';
import { DVModelContainer } from './uimanager/DynamicView';
//import { NativeModuleRegistryBuilder } from './NativeModuleRegistryBuilder';
//import { NativeModuleRegistry } from './bridge/NativeModuleRegistry';
import { CatalystInstance } from './bridge/CatalystInstance';
import { ReactFeatureFlags } from './config/ReactFeatureFlags';
import { ReactQueueConfigurationSpec } from './bridge/queue/ReactQueueConfigurationSpec';
import { CatalystInstanceImpl, CatalystInstanceImplBuilder } from './bridge/CatalystInstanceImpl';
import { ReactPackage } from './ReactPackage';
import { ReactContext } from './bridge/ReactContext';
import { JavaScriptExecutorFactory } from './bridge/JavaScriptExecutorFactory'
import { JavaScriptExecutor } from './bridge/JavaScriptExecutor'
import { JSBundleLoader } from './bridge/JSBundleLoader'
import { JSIModulePackage } from './bridge/JSIModulePackage'
import { ReactQueueConfiguration } from './bridge/queue/ReactQueueConfiguration'
import { SurfaceDelegateFactory } from './common/SurfaceDelegateFactory'
import { ReactInstanceManagerBuilder } from './ReactInstanceManagerBuilder'
import * as Communication from './common/Communication'
import { UIImplementationProvider } from './uimanager/UIImplementationProvider';

//export class Context {}
export class ReactInstanceManager {
  private static readonly TAG: string = "React ReactInstanceManager"
  private mUseFallbackBundle: boolean = false;
  //    private readonly mCreateReactContextThread = new worker.Worker("worker/test.js", {name:"first worker"})
  private mPackages: ArrayList<ReactPackage> = new ArrayList();
  private mDevSupportManager: DevSupportManager;
  private mCurrentReactContext?: ReactContext;
  private mModelContainer: DVModelContainer
  public constructor(private mContext: Context.AbilityContext,
                     private mJavaScriptExecutorFactory: JavaScriptExecutorFactory,
                     private mJSMainModulePath: string,
                     private mUIImplementationProvider: UIImplementationProvider,
                     private mJSIModulePackage: JSIModulePackage,
                     private mSurfaceDelegateFactory: SurfaceDelegateFactory,
                     private mJsBundleLoader: JSBundleLoader,
                     private mUseDeveloperSupport: boolean,
                     devSupportManagerFactory: DevSupportManagerFactory,
                     devBundleDownloadListener: DevBundleDownloadListener,
                     minNumShakes: number, customPackagerCommandHandlers: HashMap<string, RequestHandler>) {
    hilog.info(0x0000, ReactInstanceManager.TAG, 'constructor');

    this.mDevSupportManager = devSupportManagerFactory.create(mContext, this.createDevHelperInterface(), this.mJSMainModulePath,
      mUseDeveloperSupport, /*redBoxHandler,*/
      devBundleDownloadListener, minNumShakes, customPackagerCommandHandlers, mSurfaceDelegateFactory);

    // todo: implement module related functionality
    //    this.mPackages.add(new CoreModulesPackage)
    //    this.mPackages.add(new DebugCorePackage)
    //    this.mPackages.add(new MainModulesPackage)
  }

  //    static builder(): ReactInstanceManagerBuilder {
  //        return new ReactInstanceManagerBuilder();
  //    }

  //TODO
  static initializeSoLoaderIfNecessary(abilityContext: Context.AbilityContext) {

  }

  //TODO
  public onHostCreate(want: Want, launchParam: any) {
  }

  public onHostDestroy() {
  }

  public onHostWindowStageDestroy() {
  }

  public onHostForeground() {
    //todo: temperate solution; should move to createAndSetupReactContext
    // this.getCurrentReactContext().getCatalystInstance().onHostForeground()
  }

  public onHostBackground() {
    // this.getCurrentReactContext().getCatalystInstance().onHostBackground()
  }

  public getCurrentReactContext(): ReactContext {
    return this.mCurrentReactContext
  }

  public createHarmonyRootComponent(windowStage: Window.WindowStage, harmonyPageName: string) {
    hilog.info(0x0000, ReactInstanceManager.TAG, 'createHarmonyRootComponent');
    if (!harmonyPageName) {
      hilog.error(0x0000, ReactInstanceManager.TAG, 'Failed to display react root component. Cause: harmonyPageName is null');
      return;
    }

    windowStage.loadContent(harmonyPageName, (err, data) => {
      if (err.code) {
        hilog.error(0x0000, ReactInstanceManager.TAG, 'Failed to display react root component. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, ReactInstanceManager.TAG, 'Succeeded in display the react root content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
  }

  public startReactFramework(moduleName?: string, initialProperties?: Want, initialUITemplate?: string): void {
    this.createAndSetupReactContext(this.mJavaScriptExecutorFactory, this.mJsBundleLoader);
    //TODO layout related functionality
  }

  public setReactRoot(modelContainer: DVModelContainer) {
    hilog.info(0x0000, ReactInstanceManager.TAG, 'setReactRoot 444444');
    this.mModelContainer = modelContainer;
  }

  public attachReactRoot() {
    hilog.info(0x0000, ReactInstanceManager.TAG, 'attachReactRoot 444444');
    let catalystInstance = this.getCurrentReactContext().getCatalystInstance()
    catalystInstance.attachReactRoot(this.mModelContainer);
    catalystInstance.setUpMessageResponse();
  }

  public runReactApplication() {
    hilog.info(0x0000, ReactInstanceManager.TAG, 'runReactApplication');
    this.getCurrentReactContext().getCatalystInstance().runApplication(null)
  }

  //todo
  //  private processPackage(
  //    reactPackage: ReactPackage, nativeModuleRegistryBuilder: NativeModuleRegistryBuilder) {
  //    //      SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "processPackage")
  //    //          .arg("className", reactPackage.getClass().getSimpleName())
  //    //          .flush();
  //    //      if (reactPackage instanceof ReactPackageLogger) {
  //    //        ((ReactPackageLogger) reactPackage).startProcessPackage();
  //    //      }
  //    nativeModuleRegistryBuilder.processPackage(reactPackage);
  //
  //    //      if (reactPackage instanceof ReactPackageLogger) {
  //    //        ((ReactPackageLogger) reactPackage).endProcessPackage();
  //    //      }
  //    //      SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush();
  //  }

  public setUseFallbackBundle(useFallbackBundle: boolean): void {
    this.mUseFallbackBundle = useFallbackBundle;
  }

  public getUseFallbackBundle(): boolean {
    return this.mUseFallbackBundle;
  }

  public getDevSupportManager(): DevSupportManager {
    return this.mDevSupportManager;
  }

  private getJSExecutorFactory(): JavaScriptExecutorFactory {
    return this.mJavaScriptExecutorFactory;
  }

  private createDevHelperInterface(): ReactInstanceDevHelper {
    return ((instance: ReactInstanceManager) => {
      return new class temp extends ReactInstanceDevHelper {
        constructor(private mInstance: ReactInstanceManager) {
          super()
        }

        //      @Override
        public onReloadWithJSDebugger(jsExecutorFactory: JavaScriptExecutorFactory): void {
          hilog.info(0x0000, "ReactInstanceDevHelper", 'onReloadWithJSDebugger');
          this.mInstance.onReloadWithJSDebugger(jsExecutorFactory);
        }

        //      @Override
        public onJSBundleLoadedFromServer(): void {
          hilog.info(0x0000, "ReactInstanceDevHelper", 'onJSBundleLoadedFromServer');
          this.mInstance.onJSBundleLoadedFromServer();
        }

        //  @Override
        //  public void toggleElementInspector() {
        //    ReactInstanceManager.this.toggleElementInspector();
        //  }

        //  @Override
        //  public @Nullable Activity getCurrentActivity() {
        //    return ReactInstanceManager.this.mCurrentActivity;
        //  }

        // @Override
        public getJavaScriptExecutorFactory(): JavaScriptExecutorFactory {
          hilog.info(0x0000, "ReactInstanceDevHelper", 'getJavaScriptExecutorFactory');
          return this.mInstance.getJSExecutorFactory();
        }

        //  @Override
        //  public createRootView(appKey: string): null {
        //  Activity currentActivity = getCurrentActivity();
        //  if (currentActivity != null) {
        //  ReactRootView rootView = new ReactRootView(currentActivity);
        //  rootView.setIsFabric(ReactFeatureFlags.enableFabricRenderer);
        //  rootView.startReactApplication(ReactInstanceManager.this, appKey, null);
        //  return rootView;
        //}

        //return null;
        //}

        //@Override
        //public void destroyRootView(View rootView) {
        //  // TODO T62192299: remove when investigation is complete
        //  FLog.e(TAG, "destroyRootView called");
        //
        //  if (rootView instanceof ReactRootView) {
        //          // TODO T62192299: remove when investigation is complete
        //          FLog.e(TAG, "destroyRootView called, unmountReactApplication");
        //
        //          ((ReactRootView) rootView).unmountReactApplication();
        //        }
        //}
      }

      (instance);
    })(this)
  }

  //  @ThreadConfined(UI)
  private onReloadWithJSDebugger(jsExecutorFactory: JavaScriptExecutorFactory): void { //JavaJSExecutor.Factory
    hilog.info(0x0, ReactInstanceManager.TAG, "ReactInstanceManager.onReloadWithJSDebugger()");
    let jsBundleLoader= JSBundleLoader.createRemoteDebuggerBundleLoader(
      this.mDevSupportManager.getJSBundleURLForRemoteDebugging(), this.mDevSupportManager.getSourceUrl());
    this.reCreateAndSetupReactContext(this.mJavaScriptExecutorFactory, jsBundleLoader)
  }

  //@ThreadConfined(UI)
  private onJSBundleLoadedFromServer(): void {
    hilog.info(0x0, ReactInstanceManager.TAG, "ReactInstanceManager.onJSBundleLoadedFromServer()");

    let jsBundleLoader: JSBundleLoader = JSBundleLoader.createCachedBundleFromNetworkLoader(
      this.mDevSupportManager.getSourceUrl(), this.mDevSupportManager.getDownloadedJSBundleFile());
    this.reCreateAndSetupReactContext(this.mJavaScriptExecutorFactory, jsBundleLoader)
  }

  private createAndSetupReactContext(jsExecutorFactory: JavaScriptExecutorFactory, jsBundleLoader: JSBundleLoader): void {
    hilog.info(0x0, ReactInstanceManager.TAG, "createAndSetupReactContext and this.mJSMainModulePath: " + this.mJSMainModulePath);
    if (this.mUseDeveloperSupport && this.mJSMainModulePath != null) {
      if (this.mJsBundleLoader == null) {
        hilog.info(0x0, ReactInstanceManager.TAG, "mJsBundleLoader == null, so call handleReloadJS to get js from server");
        this.mDevSupportManager.handleReloadJS();
      } else {
        hilog.info(0x0, ReactInstanceManager.TAG, "check if DevSupportManager is running");

        this.mDevSupportManager.isPackagerRunning(((instanceManager: ReactInstanceManager) => {
          return new class temp extends PackagerStatusCallback {
            public constructor(private mInstanceManager: ReactInstanceManager) {
              super()
            }

            public onPackagerStatusFetched(packagerIsRunning: boolean): void { // runOnUiThread

              hilog.info(0x0, ReactInstanceManager.TAG, "onPackagerStatusFetched");
              let instanceManager = this.mInstanceManager
              let devSupportManager = instanceManager.getDevSupportManager()
              let devSettings = devSupportManager.getDevSettings();

              if (packagerIsRunning) {
                hilog.info(0x0, ReactInstanceManager.TAG, "DevSupportManager is running and handleReloadJS from PackagerStatusCallback");
                devSupportManager.handleReloadJS();
              } else if (devSupportManager.hasUpToDateJSBundleInCache()
                && !devSettings.isRemoteJSDebugEnabled()
                && !instanceManager.getUseFallbackBundle()) {
                // If there is a up-to-date bundle downloaded from server,
                // with remote JS debugging disabled, always use that.
                hilog.info(0x0, ReactInstanceManager.TAG, "onJSBundleLoadedFromServer from PackagerStatusCallback");
                instanceManager.onJSBundleLoadedFromServer();
              } else {
                // If dev server is down, disable the remote JS debugging.
                devSettings.setRemoteJSDebugEnabled(false);
                hilog.info(0x0, ReactInstanceManager.TAG, "recreateReactContextInBackgroundFromBundleLoader");
                //todo: should enable this
                instanceManager.recreateReactContextInBackgroundFromBundleLoader()
              }
            }
          }(this)
        })(this))
        return;
      }
    }

    this.recreateReactContextInBackgroundFromBundleLoader()
  }

  private recreateReactContextInBackgroundFromBundleLoader() {
    hilog.info(0x0, ReactInstanceManager.TAG, "ReactInstanceManager.recreateReactContextInBackgroundFromBundleLoader()");
    this.reCreateAndSetupReactContext(this.mJavaScriptExecutorFactory, this.mJsBundleLoader);
  }

  private reCreateAndSetupReactContext(jsExecutorFactory: JavaScriptExecutorFactory, jsBundleLoader: JSBundleLoader) {
    hilog.info(0x0000, ReactInstanceManager.TAG, 'createReactContext 0000');
    this.mCurrentReactContext = new ReactContext(this.mContext)

    let catalystInstanceBuilder = new CatalystInstanceImplBuilder()
      .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
      .setJSExecutor(jsExecutorFactory.create())
//      .setRegistry(nativeModuleRegistry)
      .setJSBundleLoader(jsBundleLoader)
//      .setJSExceptionHandler(exceptionHandler);
    hilog.info(0x0000, ReactInstanceManager.TAG, 'createReactContext 11111');

    let catalystInstance: CatalystInstance = catalystInstanceBuilder.build();
    this.mCurrentReactContext.initializeWithInstance(catalystInstance);
    catalystInstance.initialize()

    hilog.info(0x0000, ReactInstanceManager.TAG, 'createReactContext 22222');
    //todo: add some listners to notify ablitystatge change
    /*let nativeModuleRegistry = */

    this.processPackages(this.mPackages, false);
    //        if (ReactFeatureFlags.useTurboModules && this.mTMMDelegateBuilder != null) {
    //          TurboModuleManagerDelegate tmmDelegate =
    //              mTMMDelegateBuilder
    //                  .setPackages(mPackages)
    //                  .setReactApplicationContext(reactContext)
    //                  .build();
    //
    //          TurboModuleManager turboModuleManager =
    //              new TurboModuleManager(
    //                  catalystInstance.getRuntimeExecutor(),
    //                  tmmDelegate,
    //                  catalystInstance.getJSCallInvokerHolder(),
    //                  catalystInstance.getNativeCallInvokerHolder());
    //
    //          catalystInstance.setTurboModuleManager(turboModuleManager);
    //
    //          TurboModuleRegistry registry = (TurboModuleRegistry) turboModuleManager;
    //
    //          // Eagerly initialize TurboModules
    //          for (String moduleName : registry.getEagerInitModuleNames()) {
    //            registry.getModule(moduleName);
    //          }
    //        }

    //        if (this.mJSIModulePackage != null) {
    //          catalystInstance.addJSIModules(
    //              mJSIModulePackage.getJSIModules(
    //                  reactContext, catalystInstance.getJavaScriptContextHolder()));
    //        }
    //        if (ReactFeatureFlags.enableFabricRenderer) {
    //          catalystInstance.getJSIModule(JSIModuleType.UIManager);
    //        }
    //        if (mBridgeIdleDebugListener != null) {
    //          catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
    //        }
    //        if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
    //          catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
    //        }
    //
    //        ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START);
    //        Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
    this.runReactBundle()
    this.attachReactRoot()
    this.runReactApplication()
  }

  private processPackages(
    // reactContext: ReactContext,
    packages: ArrayList<ReactPackage>,
    checkAndUpdatePackageMembership: boolean) {
    hilog.info(0x0000, ReactInstanceManager.TAG, 'processPackages 111');
    let catalystInstance = this.mCurrentReactContext?.getCatalystInstance()
    catalystInstance?.processPackages(this.dispatchEventToRootView.bind(this));
    //    let nativeModuleRegistryBuilder = new NativeModuleRegistryBuilder(reactContext, this);
    //    //      for (let reactPackage of packages) {
    //    //        hilog.info(0x0000, ReactInstanceManager.TAG, 'constructor 0000000');
    //    //          if (checkAndUpdatePackageMembership && this.mPackages.has(reactPackage as ReactPackage)) {
    //    //            continue;
    //    //          }z
    //    //        hilog.info(0x0000, ReactInstanceManager.TAG, 'constructor aaaaa');
    //    ////          Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createAndProcessCustomReactPackage");
    //    //          try {
    //    //            hilog.info(0x0000, ReactInstanceManager.TAG, 'constructor bbbbbbb');
    //    //            if (checkAndUpdatePackageMembership) {
    //    //              this.mPackages.add(reactPackage);
    //    //            }
    //    //            hilog.info(0x0000, ReactInstanceManager.TAG, 'constructor ccccccc');
    //    //            this.processPackage(reactPackage, nativeModuleRegistryBuilder);
    //    //            hilog.info(0x0000, ReactInstanceManager.TAG, 'constructor ddddddd');
    //    //          } finally {
    //    ////            Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
    //    //          }
    //    //      }
    //
    //    let nativeModuleRegistry: NativeModuleRegistry =  nativeModuleRegistryBuilder.build();
    //    hilog.info(0x0000, ReactInstanceManager.TAG, 'constructor 4444444');
    //    return nativeModuleRegistry;
  }

  private runReactBundle() {
    hilog.info(0x0000, ReactInstanceManager.TAG, 'runReactBundle');
    let catalystInstance = this.getCurrentReactContext().getCatalystInstance()
    catalystInstance.runJSBundle(this.dispatchEventToRootView.bind(this));
  }

  private dispatchEventToRootView(type: string) {
    hilog.info(0x0000, ReactInstanceManager.TAG, 'dispatchEventToRootView %{public}s', type);
    let data = {
      data: {
        "type": type
      } };
    let innerEvent = {
      eventId: 1,
      //        priority: emitter.EventPriority.HIGH
    };
    emitter.emit(innerEvent, data)
  }
}
